package com.vcc.boss.controller;


import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaCheckSafe;
import cn.dev33.satoken.annotation.SaMode;
import cn.dev33.satoken.stp.StpUtil;
import cn.idev.excel.ExcelWriter;
import cn.idev.excel.write.metadata.WriteSheet;
import com.mybatisflex.core.paginate.Page;
import com.vcc.boss.core.component.RedisComponent;
import com.vcc.boss.core.component.SmsComponent;
import com.vcc.boss.core.component.UserComponent;
import com.vcc.boss.core.config.AccountConfig;
import com.vcc.boss.core.vo.user.UserSummaryVo;
import com.vcc.core.annotations.CurrentClientIp;
import com.vcc.core.annotations.RestPack;
import com.vcc.core.derive.admin.DeriveUser;
import com.vcc.core.exception.ProgramException;
import com.vcc.core.util.*;
import com.vcc.data.entity.*;
import com.vcc.service.boss.request.user.*;
import com.vcc.service.boss.response.user.UserVoBoss;
import com.vcc.service.client.vo.kyc.KycCertInfoVo;
import com.vcc.service.sa.BossUserUtil;
import com.vcc.service.service.*;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@RestController
@RequestMapping("bossUser")
public class UserController {

    private static final Logger logger = LoggerFactory.getLogger(UserController.class);
    @Resource
    private UserService userService;
    @Resource
    private AccountConfig accountConfig;
    @Resource
    private CurrencyService currencyService;
    @Resource
    private RoleService roleService;
    @Resource
    private WalletService walletService;
    @Resource
    private CardTypeService cardService;
    @Resource
    private UserCardWalletService userCardWalletService;
    @Resource
    private UserCardService userCardService;
    @Resource
    private WalletDetailService walletDetailService;
    @Resource
    private ComptrollerService comptrollerService;
    @Resource
    private SmsComponent smsComponent;
    @Resource
    private UserComponent userComponent;
    @Resource
    private RedisComponent redisComponent;
    @Resource
    private BossUserService bossUserService;
    @Autowired
    private KycCertInfoService kycCertInfoService;
    @Autowired
    private EnterpriseCertInfoService enterpriseCertInfoService;


    @SaCheckPermission(value = "client:user:list:view")
    @RestPack
    @SaCheckLogin
    @GetMapping("paging")
    public Page<UserVoBoss> paging(
            Integer pageNumber,
            Integer pageSize,
            Long targetUserId,
            String account,
            String email,
            Integer status,
            String remark,
            Long dealerId,
            Long parentId,
            String sorter
    ) {
        Page<User> paging = userService.paging(
                Page.of(
                        pageNumber == null ? 1 : pageNumber,
                        pageSize == null ? 20 : pageSize
                ),
                targetUserId,
                status,
                remark,
                account,
                email,
                dealerId,
                sorter,
                parentId
        );
        if (paging.getRecords().isEmpty()) {
            return Page.of(pageNumber == null ? 1 : pageNumber, pageSize == null ? 20 : pageSize);
        }
        List<Long> ids = paging.getRecords().stream().map(User::getId).toList();
        List<Long> dealerIds = paging.getRecords().stream().filter(it -> it.getBeLongToDealer() != null && it.getBeLongToDealer() != 0L).map(User::getBeLongToDealer).distinct().toList();
        List<Long> clientRegisterIds = paging.getRecords().stream().filter(it -> "client".equals(it.getRegisterSource()) && it.getRegisterId() != null && it.getRegisterId() != 0L).map(User::getRegisterId).distinct().toList();
        List<Long> systemRegisterIds = paging.getRecords().stream().filter(it -> "system".equals(it.getRegisterSource()) && it.getRegisterId() != null && it.getRegisterId() != 0L).map(User::getRegisterId).distinct().toList();
        List<BossUser> dealers = dealerIds.isEmpty() ? List.of() : bossUserService.listByIds(dealerIds);
        List<User> clientUsers = clientRegisterIds.isEmpty() ? List.of() : userService.listByIds(clientRegisterIds);
        List<BossUser> systemUsers = systemRegisterIds.isEmpty() ? List.of() : bossUserService.listByIds(systemRegisterIds);
        return paging.map(it -> {
            Role sourceRole = roleService.getRole(it.getId());
            String dealerAccount = dealers.stream().filter(dealer -> dealer.getId().equals(it.getBeLongToDealer())).map(BossUser::getEmail).findFirst().orElse("");
            it.setDealerAccount(dealerAccount);
            String registerAccount = "";
            if ("client".equals(it.getRegisterSource())) {
                registerAccount = clientUsers.stream().filter(user -> user.getId().equals(it.getRegisterId())).map(User::getEmail).findFirst().orElse("");
            } else if ("system".equals(it.getRegisterSource())) {
                registerAccount = systemUsers.stream().filter(user -> user.getId().equals(it.getRegisterId())).map(BossUser::getEmail).findFirst().orElse("");
            }
            List<Wallet> wallets = walletService.listByUserId(it.getId());
            if (!StpUtil.hasPermission("client:user:list:admin:remark:view")) {
                it.setAdminRemark("");
            }
            return new UserVoBoss(it, sourceRole, registerAccount, wallets);
        });
    }

    @SaCheckPermission(value = "client:user:list:derive")
    @RestPack
    @SaCheckLogin
    @GetMapping("derive")
    public void derive(
            Long targetUserId,
            String account,
            String email,
            Integer status,
            String remark,
            Long dealerId,
            String sorter,
            Long parentId,
            HttpServletResponse response
    ) {
        Long pages = 1L;
        String fileName = System.currentTimeMillis() + "用户详情.xlsx";
        ExcelWriter excelWriter;
        try {
            ExportUtil.setExcelResponseHeaders(response, fileName);
            excelWriter = ExportUtil.createExcelWriter(response.getOutputStream(), DeriveUser.class);
        } catch (Exception e) {
            logger.error("导出用户数据失败", e);
            throw new ProgramException("导出用户数据失败");
        }
        WriteSheet writeSheet = ExportUtil.createWriteSheet("用户数据", DeriveUser.class);
        Page<User> paging;
        do {
            paging = userService.paging(Page.of(pages++, 1000), targetUserId, status, remark, account, email, dealerId, sorter,parentId);
            List<Long> ids = paging.getRecords().stream().map(User::getId).toList();
            List<Long> clientRegisterIds = paging.getRecords().stream().filter(it -> "client".equals(it.getRegisterSource()) && it.getRegisterId() != null && it.getRegisterId() != 0L).map(User::getRegisterId).distinct().toList();
            List<Long> systemRegisterIds = paging.getRecords().stream().filter(it -> "system".equals(it.getRegisterSource()) && it.getRegisterId() != null && it.getRegisterId() != 0L).map(User::getRegisterId).distinct().toList();
            List<Wallet> wallets = ids.isEmpty() ? List.of() : walletService.listByUserIds(ids);
            List<User> clientUsers = clientRegisterIds.isEmpty() ? List.of() : userService.listByIds(clientRegisterIds);
            List<BossUser> systemUsers = systemRegisterIds.isEmpty() ? List.of() : bossUserService.listByIds(systemRegisterIds);
            //释放内存
            ids = null;
            clientRegisterIds = null;
            systemRegisterIds = null;
            List<DeriveUser> list = paging.getRecords().stream().map(it -> {
                Role role = roleService.getRole(it.getId());
                Boolean kycStatus = kycCertInfoService.existsByUserId(it.getId());
                Boolean kybStatus = enterpriseCertInfoService.existsByUserId(it.getId());
                Integer certStatus = 2;
                String keyType = "未提交认证";
                Date requestCertDate = null;
                if (!kycStatus && !kybStatus) {
                    keyType = "暂未提交";
                    certStatus = 3;
                }
                if (kycStatus) {
                    keyType = "个人认证";
                    KycCertInfoVo certInfoVo = kycCertInfoService.getByUserId(it.getId());
                    certStatus = certInfoVo.getStatus();
                    requestCertDate = certInfoVo.getCreateDate();
                }
                if (kybStatus) {
                    keyType = "企业认证";
                    EnterpriseCertInfo enterpriseCertInfo = enterpriseCertInfoService.getByUserId(it.getId());
                    certStatus = enterpriseCertInfo.getStatus();
                    requestCertDate = enterpriseCertInfo.getCreateDate();
                }
                Wallet usdWallet = wallets.stream().filter(wallet -> wallet.getUserId().equals(it.getId()) && wallet.getCurrencyCode().equals("USD")).findFirst().orElse(null);
                String registerAccount = "";
                if ("client".equals(it.getRegisterSource())) {
                    registerAccount = clientUsers.stream().filter(user -> user.getId().equals(it.getRegisterId())).map(User::getEmail).findFirst().orElse("");
                } else if ("system".equals(it.getRegisterSource())) {
                    registerAccount = systemUsers.stream().filter(user -> user.getId().equals(it.getRegisterId())).map(BossUser::getEmail).findFirst().orElse("");
                } else {
                    registerAccount = "";
                }
                return DeriveUser.builder()
                        .id(it.getId())
                        .username(it.getUsername())
                        .email(it.getEmail())
                        .registerDate(it.getCreateDate())
                        .parentId(it.getParentId())
                        .parentAccount(it.getParentAccount())
                        .status(it.getStatus())
                        .remark(it.getRemark())
                        .dealerId(it.getParentId())
                        .dealerAccount(it.getDealerAccount())
                        .roleName(role == null ? "" : role.getName())
                        .certType(keyType)
                        .certStatus(certStatus)
                        .certSubmitDate(requestCertDate)
                        .rechargeRate(usdWallet == null ? BigDecimal.ZERO : usdWallet.getExchangeRate())
                        .withdrawRate(usdWallet == null ? BigDecimal.ZERO : usdWallet.getExchangeRate())
                        .balance(usdWallet == null ? BigDecimal.ZERO : usdWallet.getUsableQuota())
                        .registerCount(userService.countByRegisterId(it.getId()))
                        .openCardCount(userCardService.countByUserId(it.getId()))
                        .registerAccount(registerAccount)
                        .build();
            }).toList();
            ExportUtil.writeData(excelWriter, writeSheet, list);
            list = null;
        } while (!(pages).equals(paging.getTotalPage() + 1));
        pages = null;
        ExportUtil.closeExcelWriter(excelWriter);

    }

    @SaCheckPermission(value = "client:user:list:view")
    @RestPack
    @SaCheckLogin
    @GetMapping("listDealerAccount")
    public List<UserParamVoBoss> listByAccount() {
        return userService.listDealer().stream().map(UserParamVoBoss::new).collect(Collectors.toList());
    }

    @SaCheckPermission(value = "client:user:search")
    @RestPack
    @SaCheckLogin
    @GetMapping("listByEmail")
    public List<UserParamVoBoss> listByAccount(
            String email
    ) {
        return userService.listByEmail(email).stream().map(UserParamVoBoss::new).collect(Collectors.toList());
    }

    @SaCheckPermission(value = {"client:user:list:status:edit", "client:user:list:risk:status"}, mode = SaMode.OR)
    @RestPack
    @SaCheckLogin
    @PostMapping("updateStatus")
    public Boolean updateStatus(
            HttpServletRequest request,
            @RequestBody @Validated UpdateUserStatusDto updateUserStatusDto) {
        if (RegexUtil.checkObjectIsNull(updateUserStatusDto.getTargetId())) {
            throw new ProgramException("操作用户不能为空");
        }
        if (RegexUtil.checkObjectIsNull(updateUserStatusDto.getStatus())) {
            throw new ProgramException("操作状态不能为空");
        }
        User user = userService.getById(updateUserStatusDto.getTargetId());
        if (!BossUserUtil.hasAllData() && !user.getBeLongToDealer().equals(BossUserUtil.getId())) {
            throw new ProgramException("无权处理该用户的参数配置");
        }
        if (user.getStatus().equals(4)) {
            throw new ProgramException("当前用户待激活无法操作");
        }
        if (user.getStatus().equals(3)) {
            throw new ProgramException("当前用户已注销无法启用");
        }
        if (user.getStatus().equals(updateUserStatusDto.getStatus())) {
            throw new ProgramException("状态一致无需修改");
        }
        // 0 可用 1 禁用  2  管理员禁用 3 注销 4 待激活
        //true冻结 false 解冻
        if (updateUserStatusDto.getStatus()) {
            if (user.getParentId() == null || user.getParentId() == 0) {
                userService.updateStatusByParentId(updateUserStatusDto.getTargetId(), 1);
            }
            userService.bossUpdateStatusById(updateUserStatusDto.getTargetId(), StpUtil.hasPermission("client:user:list:risk:status") ? 2 : 1);
            comptrollerService.unBlock(IpUtil.getIp(request), user.getId(), 0);
        } else {
            if (user.getStatus().equals(2)) {
                if (!StpUtil.hasPermission("client:user:list:risk:status")) {
                    throw new ProgramException("当前用户处于风险状态无法解冻");
                }
                userService.bossUpdateStatusById(updateUserStatusDto.getTargetId(), 0);
            } else {
                userService.bossUpdateStatusById(updateUserStatusDto.getTargetId(), 0);
            }
            comptrollerService.block(IpUtil.getIp(request), user.getId(), 0);
        }
        return true;
    }

    @SaCheckPermission(value = "client:user:list:password:edit")
    @RestPack
    @SaCheckLogin
    @PostMapping("updatePassword")
    @SaCheckSafe("UPDATE_CLIENT_USER_PASSWORD")
    public Boolean updatePassword(
            @CurrentClientIp String clientIp,
            @RequestBody @Validated UpdateUserPasswordDto updateUserPasswordDto) {
        StpUtil.closeSafe("UPDATE_CLIENT_USER_PASSWORD");
        if (RegexUtil.checkObjectIsNull(updateUserPasswordDto.getPassword())) {
            throw new ProgramException("密码不能为空");
        }
        User user = userService.getById(updateUserPasswordDto.getId());
        if (!BossUserUtil.hasAllData() && !user.getBeLongToDealer().equals(BossUserUtil.getId())) {
            throw new ProgramException("无权处理该用户的参数配置");
        }
        if (user.getPassword().equals(EncryptUtil.encryptValue(updateUserPasswordDto.getPassword(), user.getSalt()))) {
            throw new ProgramException("前后密码一致无需修改");
        }
        return userService.updatePasswordById(user.getId(), EncryptUtil.encryptValue(updateUserPasswordDto.getPassword(), user.getSalt()));
    }

    @SaCheckPermission(value = "client:user:list:google:edit")
    @RestPack
    @SaCheckLogin
    @PostMapping("clearGoogle")
    @SaCheckSafe("CLEAR_CLIENT_USER_GOOGLE")
    public Boolean clearGoogle(
            @RequestBody @Validated ClearUserGoogleDto updateUserPasswordDto) {
        StpUtil.closeSafe("CLEAR_CLIENT_USER_GOOGLE");
        User user = userService.getById(updateUserPasswordDto.getId());
        if (!BossUserUtil.hasAllData() && !user.getBeLongToDealer().equals(BossUserUtil.getId())) {
            throw new ProgramException("无权处理该用户的参数配置");
        }
        return userService.clearGoogle(user.getId());
    }

    @SaCheckPermission(value = "client:user:list:remark:edit")
    @RestPack
    @SaCheckLogin
    @PostMapping("updateRemark")
    public Boolean updateRemark(
            @RequestBody @Validated UpdateUserRemarkDto updateUserRemarkDto) {
        User user = userService.getById(updateUserRemarkDto.getTargetId());
        if (!BossUserUtil.hasAllData() && !user.getBeLongToDealer().equals(BossUserUtil.getId())) {
            throw new ProgramException("无权处理该用户的参数配置");
        }
        return userService.updateRemarkById(updateUserRemarkDto.getTargetId(), updateUserRemarkDto.getRemark());
    }

    @SaCheckPermission(value = "client:user:list:admin:remark:edit")
    @RestPack
    @SaCheckLogin
    @PostMapping("updateAdminRemark")
    public Boolean updateAdminRemark(
            @RequestBody @Validated UpdateUserRemarkDto updateUserRemarkDto) {
        User user = userService.getById(updateUserRemarkDto.getTargetId());
        if (!BossUserUtil.hasAllData() && !user.getBeLongToDealer().equals(BossUserUtil.getId())) {
            throw new ProgramException("无权处理该用户的参数配置");
        }
        return userService.updateAdminRemarkById(updateUserRemarkDto.getTargetId(), updateUserRemarkDto.getRemark());
    }


    @SaCheckPermission("client:user:account:summary")
    @RestPack
    @SaCheckLogin
    @GetMapping("getAccountSummaryData")
    public UserSummaryVo getAccountSummaryData() {
        Map<String, BigDecimal> stringBigDecimalMap = walletService.summaryBalance();
        Map<String, Long> userSummary = userService.userSummary();
        return UserSummaryVo.builder()
                .positiveNumber(stringBigDecimalMap.getOrDefault("positiveNumber", BigDecimal.ZERO))
                .negativeNumber(stringBigDecimalMap.getOrDefault("negativeNumber", BigDecimal.ZERO))
                .totalCount(userSummary.getOrDefault("totalCount", 0L))
                .activeCount(userSummary.getOrDefault("activeCount", 0L))
                .freezeCount(userSummary.getOrDefault("freezeCount", 0L))
                .riskCount(userSummary.getOrDefault("riskCount", 0L))
                .closeCount(userSummary.getOrDefault("closeCount", 0L))
                .build();
    }

}
